#ifndef __DBR_H__
  #define __DBR_H__

#include <string>

#include "object_record_set.h"
#include "global_variables.h"

using namespace std;

//! Relations between objects of database_serializable made easy! (database relationships)

/*!
here are the rule(s):
\li every table [in the database] must have an id column (unsigned long long)

\li every database_serializable object must have the following method:
  \code
  public:
    static const char *get_table_name() {
      return "table1";
    }
  \endcode

\li we assume that each foreign-key has the following format:
  \code
  T::get_table_name() + "_id"
  \endcode
  or you can pass the name of the foreign-key when calling the load method.
*/

namespace dbr {

  //! An implementation for one to one 1-1 relationship

  /*!
  Implementation date: 2009

  Usage:

  \code
  const unsigned long long a_person_id = 9ULL;
  teacher t1;
  one_to_one<person, teacher>::load(a_person_id, t1); // t1 will be loaded from database
  // or
  const unsigned long long a_person_id = 9ULL;
  person p1;
  teacher t1;
  one_to_one<person, teacher>::load(a_person_id, t1, p1); // t1 & p1 will be loaded from database
  \endcode
  */

  template <class T, class U>
  class one_to_one {
  public:
    static void load(unsigned long long id, U &output1, string foreign_key_name = global_empty_string) {
      if (foreign_key_name.empty() == true) {
        foreign_key_name = T::get_table_name();
        foreign_key_name += "_id"; // default fk format "<class_name>_id"
      } // if

      string tmp = "SELECT id FROM %1 WHERE %2=%3";
      str::format(tmp, arg<const char*, string, unsigned long long>(output1.get_class_name(), foreign_key_name, id));

      record_set rs;
      database_serializable::get_database()->execute_query(tmp, rs);

      if (rs.get_row_count() == 1) {
        const char *p = rs(0, (unsigned int) 0);
        if (p != 0) {
          output1.set_id(converter::to_unsigned_long_long(p));
          output1.load();
        } // if
      } // if
    }

    static void load(unsigned long long id, U &output1, T &output2, string foreign_key_name = global_empty_string) {
      output2.set_id(id);
      output2.load();
      load(id, output1, foreign_key_name);
    }
  };

  //! An implementation for one to many 1-n relationship

  /*!
  Implementation date: 2009

  Usage:

  \code
  const unsigned long long a_person_id = 9ULL;
  object_record_set<object> objs;
  one_to_many<person, object>::load(a_person_id, objs); // objs will be loaded from database
  // or
  const unsigned long long a_person_id = 9ULL;
  person p1;
  object_record_set<object> objs;
  one_to_many<person, object>::load(a_person_id, objs, p1); // objs & p1 will be loaded from database
  \endcode
  */

  template <class T, class U>
  class one_to_many {
  public:
    static void load(unsigned long long id, object_record_set<U> &output1, string foreign_key_name = global_empty_string) {
      if (foreign_key_name.empty() == true) {
        foreign_key_name = T::get_table_name();
        foreign_key_name += "_id"; // default fk format "<class_name>_id"
      } // if

      output1.get_objects("FROM %1 WHERE %2=%3", arg<const char*, string, unsigned long long>(U::get_table_name(), foreign_key_name, id));
    }

    static void load(unsigned long long id, object_record_set<U> &output1, T &output2, string foreign_key_name = global_empty_string) {
      output2.set_id(id);
      output2.load();
      load(id, output1, foreign_key_name);
    }
  };

} // namespace dbr

#endif
